package wiki.xsx.core.jmeter.core;

import wiki.xsx.core.jmeter.core.threadgroup.JmeterThreadGroup;
import wiki.xsx.core.jmeter.core.util.JmeterOptional;
import lombok.Data;
import lombok.experimental.Accessors;
import org.apache.jmeter.config.Argument;
import org.apache.jmeter.config.Arguments;
import org.apache.jmeter.config.gui.ArgumentsPanel;
import org.apache.jmeter.control.gui.TestPlanGui;
import org.apache.jmeter.testelement.TestElement;
import org.apache.jmeter.testelement.TestPlan;
import org.apache.jorphan.collections.HashTree;
import org.apache.jorphan.collections.ListedHashTree;

import java.util.List;
import java.util.Map;

/**
 * 测试计划
 *
 * @author xsx
 * @date 2022/8/22
 * @since 1.8
 * <p>
 * Copyright (c) 2022 xsx All Rights Reserved.
 * easy-jmeter is licensed under Mulan PSL v2.
 * You can use this software according to the terms and conditions of the Mulan PSL v2.
 * You may obtain a copy of Mulan PSL v2 at:
 * http://license.coscl.org.cn/MulanPSL2
 * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
 * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
 * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
 * See the Mulan PSL v2 for more details.
 * </p>
 */
@Data
@Accessors(chain = true)
public class JmeterTestPlan {

    /**
     * 测试类名称
     */
    private static final String TEST_CLASS_NAME = TestPlan.class.getName();
    /**
     * GUI类名称
     */
    private static final String GUI_CLASS_NAME = TestPlanGui.class.getName();
    /**
     * 自定义变量参数测试类名称
     */
    private static final String VARIABLES_ARGUMENTS_TEST_CLASS_NAME = Arguments.class.getName();
    /**
     * 自定义变量参数GUI类名称
     */
    private static final String VARIABLES_ARGUMENTS_GUI_CLASS_NAME = ArgumentsPanel.class.getName();

    /**
     * 配置文件路径
     */
    private String propertiesPath;
    /**
     * 测试计划名称
     */
    private String name;
    /**
     * 测试计划注释
     */
    private String comment;
    /**
     * 是否结束后拆除
     */
    private Boolean isTearDownOnShutdown;
    /**
     * 是否顺序执行（当一个线程组结束后启动下一个）
     */
    private Boolean isSerialized;
    /**
     * 自定义变量
     */
    private Map<String, String> variables;
    /**
     * 第三方类路径（仅支持导出时使用）
     */
    private List<String> classPathList;
    /**
     * 线程组
     */
    private JmeterThreadGroup threadGroup;

    /**
     * 无参构造
     */
    private JmeterTestPlan() {
        this.initPropertiesPath();
    }

    /**
     * 获取测试计划实例
     *
     * @return 返回测试计划实例
     */
    public static JmeterTestPlan getInstance() {
        return new JmeterTestPlan();
    }

    /**
     * 创建测试计划
     *
     * @return 返回测试计划配置树
     */
    public HashTree create() {
        // 如果线程组工厂为空，则提示错误信息
        if (this.threadGroup == null) {
            // 提示错误信息
            throw new IllegalArgumentException("the thread group factory can not be null");
        }
        // 创建测试计划配置树
        HashTree planTree = new ListedHashTree();
        // 设置线程组
        planTree.put(this.createTestPlan(), this.threadGroup.create());
        // 返回测试计划配置树
        return planTree;
    }

    /**
     * 创建测试计划
     *
     * @return 返回测试计划
     */
    private TestPlan createTestPlan() {
        // 创建测试计划
        TestPlan plan = new TestPlan();
        // 设置测试类名称
        plan.setProperty(TestElement.TEST_CLASS, TEST_CLASS_NAME);
        // 设置GUI类名称
        plan.setProperty(TestElement.GUI_CLASS, GUI_CLASS_NAME);
        // 设置启用
        plan.setEnabled(true);
        // 设置测试计划名称
        plan.setName(JmeterOptional.ofNullable(this.name).orElse("测试计划"));
        // 设置测试计划注释
        plan.setComment(JmeterOptional.ofNullable(this.comment).orElse(""));
        // 设置是否独立执行线程组（当一个线程组结束后启动下一个）
        plan.setSerialized(JmeterOptional.ofNullable(this.isSerialized).orElse(Boolean.FALSE));
        // 设置是否结束后拆除
        plan.setTearDownOnShutdown(JmeterOptional.ofNullable(this.isTearDownOnShutdown).orElseGet(() -> this.variables != null));
        // 设置是否功能测试模式（影响性能）
        plan.setFunctionalMode(false);
        // 设置自定义变量
        plan.setUserDefinedVariables(this.initVariables());
        // 设置第三方类路径
        plan.setTestPlanClasspathArray(this.initClassPath());
        // 返回测试计划
        return plan;
    }

    /**
     * 初始化配置文件路径
     */
    private void initPropertiesPath() {
        // 如果配置文件路径为空，则重置配置文件路径
        if (this.propertiesPath == null) {
            // 重置配置文件路径为当前路径
            this.propertiesPath = ".";
        }
    }

    /**
     * 初始化自定义变量
     *
     * @return 返回变量参数
     */
    private Arguments initVariables() {
        // 创建参数
        Arguments arguments = new Arguments();
        // 如果自定义变量不为空
        if (this.variables != null) {
            // 设置测试类名称
            arguments.setProperty(TestElement.TEST_CLASS, VARIABLES_ARGUMENTS_TEST_CLASS_NAME);
            // 设置GUI类名称
            arguments.setProperty(TestElement.GUI_CLASS, VARIABLES_ARGUMENTS_GUI_CLASS_NAME);
            // 设置启用
            arguments.setEnabled(true);
            // 添加变量
            this.variables.forEach((k, v) -> arguments.addArgument(new Argument(k, v)));
        }
        // 返回参数
        return arguments;
    }

    /**
     * 初始化第三方类路径
     *
     * @return 返回第三方类路径字符串
     */
    private String[] initClassPath() {
        // 如果第三方类路径不为空，则转为字符串
        if (this.classPathList != null) {
            // 返回字符串数组
            return this.classPathList.toArray(new String[0]);
        }
        // 返回空字符串数组
        return new String[0];
    }
}
